package com.flyqiu.flow.core.manage;


import com.flyqiu.common.tools.StringTools;
import com.flyqiu.common.user.FlyQiuUser;
import com.flyqiu.flow.api.candidate.FlowCandidateContext;
import com.flyqiu.flow.api.candidate.FlowCandidateLoad;
import com.flyqiu.flow.api.candidate.FlowInstCandidateContext;
import com.flyqiu.flow.api.candidate.FlyQiyFlowCandidateManage;
import com.flyqiu.flow.bpmn.base.ElementCandidateConfig;
import com.flyqiu.flow.core.candidate.DefaultFlowCandidateContext;
import com.flyqiu.flow.core.candidate.DefaultFlowInstCandidateContext;
import com.flyqiu.flow.bpmn.element.UserTask;

import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Slf4j
public class DefaultFlyQiuFlyCandidateManage implements FlyQiyFlowCandidateManage<DefaultFlowCandidateContext, DefaultFlowInstCandidateContext> {
    private final Map<String, Map<String, FlowCandidateLoad<?, DefaultFlowCandidateContext, DefaultFlowInstCandidateContext>>> candidateMap = new HashMap<>();


    @Override
    public DefaultFlowCandidateContext createCandidateContext(FlowCandidateContext context) {
        if (!(context instanceof DefaultFlowCandidateContext)) {
            throw new IllegalArgumentException("Context is not an instance of DefaultFlowCandidateContext");
        }
        return (DefaultFlowCandidateContext) context;
    }

    @Override
    public DefaultFlowInstCandidateContext createCandidateContext(FlowInstCandidateContext context) {
        if (!(context instanceof DefaultFlowInstCandidateContext)) {
            throw new IllegalArgumentException("Context is not an instance of DefaultFlowInstCandidateContext");
        }
        return (DefaultFlowInstCandidateContext) context;
    }

    /**
     * 注册候选人加载器
     */
    public void register(FlowCandidateLoad<?, DefaultFlowCandidateContext, DefaultFlowInstCandidateContext> loadCandidate) {
        String clazzName = loadCandidate.getClass().getName();
        String categoryKey = loadCandidate.getGroupKey();
        if (StringTools.isEmpty(categoryKey)) {
            throw new IllegalArgumentException(String.format("class : %s.categoryKey() Cannot be null empty", clazzName));
        }
        if (!candidateMap.containsKey(categoryKey)) {
            candidateMap.put(categoryKey, new HashMap<>());
        }
        String candidateKey = loadCandidate.getValue();
        if (StringTools.isEmpty(candidateKey)) {
            throw new IllegalArgumentException(String.format("class : %s.candidateKey() Cannot be null empty", clazzName));
        }
        Map<String, FlowCandidateLoad<?, DefaultFlowCandidateContext, DefaultFlowInstCandidateContext>> categoryKeyMap = candidateMap.get(categoryKey);
        if (categoryKeyMap.containsKey(candidateKey)) {
            String firstClazzName = categoryKeyMap.get(candidateKey).getClass().getName();
            String format = String.format("The same key:%s already exists. First class:%s, Your class:%s", candidateKey, firstClazzName, clazzName);
            throw new IllegalArgumentException(format);
        }
        categoryKeyMap.put(candidateKey, loadCandidate);
    }


    /**
     * 加载候选人
     *
     * @param candidateContext 候选人上下文环境
     * @return {@link  FlyQiuUser}
     */
    @Override
    public Collection<? extends FlyQiuUser> loadInstCandidates(DefaultFlowInstCandidateContext candidateContext) {
        UserTask userTask = candidateContext.getUserTask();
        if (userTask == null) {
            // 记录日志并返回空集合
            log.error("Failed to load instance candidates: UserTask is null in context.");
            return Collections.emptyList();
        }
        List<ElementCandidateConfig> candidateConfigs = userTask.getCandidateConfigs();
        if (candidateConfigs == null || candidateConfigs.isEmpty()) {
            return Collections.emptyList();
        }

        List<FlyQiuUser> result = new ArrayList<>();
        for (ElementCandidateConfig config : candidateConfigs) {
            FlowCandidateLoad<?, DefaultFlowCandidateContext, DefaultFlowInstCandidateContext> procLoadCandidate = getProcLoadCandidate(config.getCategoryKey(), config.getCandidateKey());
            if (procLoadCandidate == null) {
                // 可以选择记录日志或继续处理下一个配置
                log.warn("No matching candidate loader found for categoryKey={}, candidateKey={}", config.getCategoryKey(), config.getCandidateKey());
                continue;
            }
            Collection<? extends FlyQiuUser> users = procLoadCandidate.loadInstCandidates(candidateContext);
            result.addAll(users);
        }
        return result;
    }


    @Override
    public Collection<? extends FlyQiuUser> loadElementCandidates(DefaultFlowCandidateContext candidateContext) {
        UserTask userTask = candidateContext.getUserTask();
        if (userTask == null) {
            log.error("Failed to load element candidates: UserTask is null in context.");
            return Collections.emptyList();
        }
        List<ElementCandidateConfig> candidateConfigs = userTask.getCandidateConfigs();
        if (candidateConfigs == null || candidateConfigs.isEmpty()) {
            return Collections.emptyList();
        }

        List<FlyQiuUser> result = new ArrayList<>();
        for (ElementCandidateConfig config : candidateConfigs) {
            FlowCandidateLoad<?, DefaultFlowCandidateContext, DefaultFlowInstCandidateContext> procLoadCandidate = getProcLoadCandidate(config.getCategoryKey(), config.getCandidateKey());
            if (procLoadCandidate == null) {
                log.warn("No matching candidate loader found for categoryKey={}, candidateKey={}", config.getCategoryKey(), config.getCandidateKey());
                continue;
            }
            Collection<? extends FlyQiuUser> users = procLoadCandidate.loadElementCandidates(candidateContext);

            result.addAll(users);
        }
        return result;
    }


    /**
     * 获取候选人加载器（改进错误处理）
     *
     * @param categoryKey  加载器分组key
     * @param candidateKey 加载器唯一标识key
     * @return {@link FlowCandidateLoad} 或者 null 如果找不到对应的加载器
     */
    public FlowCandidateLoad<?, DefaultFlowCandidateContext, DefaultFlowInstCandidateContext> getProcLoadCandidate(String categoryKey, String candidateKey) {
        if (StringTools.isEmpty(categoryKey)) {
            categoryKey = "";
        }
        if (StringTools.isEmpty(candidateKey)) {
            candidateKey = "";
        }
        Map<String, FlowCandidateLoad<?, DefaultFlowCandidateContext, DefaultFlowInstCandidateContext>> categoryMap = candidateMap.get(categoryKey);
        return categoryMap != null ? categoryMap.get(candidateKey) : null;
    }
}
